package log;

import javax.swing.*;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.logging.Logger;
import java.util.ArrayList;

public class LogItemList
{
    public interface EventListener {
        void OnLogChanged(LogItemList host, List<LogItem> newItems);
    }

    public void SetEventListener(EventListener listener) {
        _eventListener = listener;
    }

    public void attachStream(ILogSteam stream) {
        if (_logStream != null) {
            close();
        }

        _logStream = stream;
        if (stream != null) {
            _readThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    readThread();
                }
            }, "ReadLogThread");
            _readThread.start();
        }
    }

    public void close() {
        Logger.getGlobal().info("log item list close log stream");
        if (_logStream != null) {
            _logStream.close();
            _logStream = null;
        }

        _readThread.interrupt();
        try {
            _readThread.join();
        } catch (InterruptedException e) {
            Logger.getGlobal().info("interrupted when wait read thread exits");
        }
        finally {
            _readThread = null;
        }
    }

    public ArrayList<LogItem> getLogList() {
        return _logItemList;
    }

    private void readThread() {

        final int LogReadCacheSize = 1024;
        final int LogReadCacheInterval = 500;

        long prevFlushTime = System.currentTimeMillis();

        ArrayList<LogItem> itemList = new ArrayList<>(LogReadCacheSize);;
        try {
            ILogSteam logStream = _logStream;
            for (LogItem item = logStream.read(); item != null; item = logStream.read())
            {
                itemList.add(item);

                long interval = System.currentTimeMillis() - prevFlushTime;
                if (itemList.size() >= LogReadCacheSize || interval >= LogReadCacheInterval) {
                    prevFlushTime = System.currentTimeMillis();
                    flushReadingLogs(itemList);
                    itemList = new ArrayList<>(LogReadCacheSize);
                }
            }
            // reach end of the stream, flush
            flushReadingLogs(itemList);
        }
        catch (Exception e) {
            Logger.getGlobal().info(String.format("read thread exception: %s", e.getMessage()));
            flushReadingLogs(itemList);
        }
        Logger.getGlobal().info(String.format("read thread exits"));
    }

    private void flushReadingLogs(ArrayList<LogItem> itemList) {

        WeakReference<LogItemList> weakSelf = new WeakReference<>(this);
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                LogItemList _self = weakSelf.get();
                if (_self == null) {
                    return;
                }
                if (itemList != null && !itemList.isEmpty()) {
                    _logItemList.addAll(itemList);
                    doLogChange(itemList);
                }
            }
        });
    }

    private void doLogChange(List<LogItem> newItems) {
        if (_eventListener != null) {
            _eventListener.OnLogChanged(this, newItems);
        }
    }

    private Thread _readThread;
    private ILogSteam _logStream;

    private EventListener _eventListener;

    private final ArrayList<LogItem> _logItemList = new ArrayList<LogItem>();
}